﻿using System;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using FirstFloor.Documents.Fonts;
using SampleBrowser.Util;

namespace SampleBrowser.Views
{
    public partial class SampleGlyphsMetrics : Page
    {
        private Rectangle currentRectangle;

        public SampleGlyphsMetrics()
        {
            InitializeComponent();

            var fonts = FontResourceInfo.AllFonts(GetType().Assembly).ToArray();
            this.cmbFont.ItemsSource = fonts;
            this.cmbFont.SelectedItem = fonts.First();
        }

        private void cmbFont_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            var font = (FontResourceInfo)this.cmbFont.SelectedItem;
            if (font != null) {
                ShowFont(font.FontStream);
            }
        }

        private void FromDisk_Click(object sender, RoutedEventArgs e)
        {
            try {
                var stream = FontHelper.OpenFontFromDisk();
                if (stream != null) {
                    ShowFont(stream);

                    this.cmbFont.SelectedValue = null;
                }
            }
            catch (Exception error) {
                new ErrorWindow(error).Show();
            }
        }

        private void ShowFont(Stream fontStream)
        {
            this.rectangles.Children.Clear();
            this.metrics.Children.Clear();

            RenderGlyphMetrics(this.sampleText, fontStream);
            RenderGlyphMetrics(this.sampleText2, fontStream);
            RenderGlyphMetrics(this.sampleText3, fontStream);
            RenderGlyphMetrics(this.sampleText4, fontStream);
        }

        private void RenderGlyphMetrics(Glyphs glyphs, Stream fontStream)
        {
            glyphs.FontSource = new FontSource(fontStream);

            var glyphRun = GlyphHelper.CreateGlyphRun(glyphs, fontStream);

            // render baseline
            DrawLine(glyphRun.Bounds.Left,
                     glyphs.OriginY,
                     glyphRun.Bounds.Right,
                     glyphs.OriginY,
                     new SolidColorBrush(Colors.Red), "baseline");

            // render ascender height
            DrawLine(glyphRun.Bounds.Left,
                     glyphs.OriginY - glyphRun.GlyphTypeface.Ascender * glyphRun.FontRenderingEmSize,
                     glyphRun.Bounds.Right,
                     glyphs.OriginY - glyphRun.GlyphTypeface.Ascender * glyphRun.FontRenderingEmSize,
                     new SolidColorBrush(Colors.Blue), "ascender height");

            // render descender height
            DrawLine(glyphRun.Bounds.Left,
                     glyphs.OriginY - glyphRun.GlyphTypeface.Descender * glyphRun.FontRenderingEmSize,
                     glyphRun.Bounds.Right,
                     glyphs.OriginY - glyphRun.GlyphTypeface.Descender * glyphRun.FontRenderingEmSize,
                     new SolidColorBrush(Colors.Green), "descender height");

            // render cap height (if exists in font)
            var capHeight = glyphRun.GlyphTypeface.CapHeight;
            if (capHeight.HasValue) {
                DrawLine(glyphRun.Bounds.Left,
                         glyphs.OriginY - capHeight.Value * glyphRun.FontRenderingEmSize,
                         glyphRun.Bounds.Right,
                         glyphs.OriginY - capHeight.Value * glyphRun.FontRenderingEmSize,
                         new SolidColorBrush(Colors.Brown), "cap height");
            }

            // render x-height (if exists in font)
            var xHeight = glyphRun.GlyphTypeface.XHeight;
            if (xHeight.HasValue) {
                DrawLine(glyphRun.Bounds.Left,
                         glyphs.OriginY - xHeight.Value * glyphRun.FontRenderingEmSize,
                         glyphRun.Bounds.Right,
                         glyphs.OriginY - xHeight.Value * glyphRun.FontRenderingEmSize,
                         new SolidColorBrush(Colors.Magenta), "x-height");
            }


            // render advance widths for each individual glyph
            var offset = glyphRun.Bounds.Left;

            DrawLine(offset,
                     glyphRun.Bounds.Top,
                     offset,
                     glyphRun.Bounds.Bottom,
                     new SolidColorBrush(Colors.LightGray));

            var prevX = offset;
            var prevY = glyphRun.Bounds.Top;

            foreach (var width in glyphRun.AdvanceWidths) {
                offset += (width * glyphRun.FontRenderingEmSize);

                DrawMouseOverRectangle(prevX, prevY, offset, glyphRun.Bounds.Bottom);

                DrawLine(offset,
                         glyphRun.Bounds.Top,
                         offset,
                         glyphRun.Bounds.Bottom,
                         new SolidColorBrush(Colors.LightGray));

                prevX = offset;
            }
        }

        private void DrawLine(double x1, double y1, double x2, double y2, Brush brush)
        {
            DrawLine(x1, y1, x2, y2, brush, null);
        }
        
        private void DrawLine(double x1, double y1, double x2, double y2, Brush brush, string text)
        {
            var line = new Line() {
                X1 = (int)x1 + .5,
                X2 = (int)x2 + .5,
                Y1 = (int)y1 + .5,
                Y2 = (int)y2 + .5,
                Stroke = brush
            };
            this.metrics.Children.Add(line);

            if (text != null) {
                var block = new TextBlock() {
                    Text = text,
                    FontSize = 9,
                    Foreground = brush
                };
                Canvas.SetLeft(block, x2 + 4);
                Canvas.SetTop(block, y2 - (block.ActualHeight / 2));
                this.metrics.Children.Add(block);
            }
        }

        private void DrawMouseOverRectangle(double x1, double y1, double x2, double y2)
        {
            var rectangle = new Rectangle() {
                Width = Math.Abs(x2 - x1),
                Height = Math.Abs(y2 - y1),
                Fill = new SolidColorBrush(Colors.Transparent)
            };
            Canvas.SetLeft(rectangle, x1);
            Canvas.SetTop(rectangle, y1);

            this.rectangles.Children.Add(rectangle);
        }

        private void OnMouseMove(object sender, MouseEventArgs e)
        {
            var r = e.OriginalSource as Rectangle;

            if (r != null && r != this.currentRectangle) {
                if (this.currentRectangle != null) {
                    this.currentRectangle.Fill = new SolidColorBrush(Colors.Transparent);
                }
                r.Fill = (SolidColorBrush)App.Current.Resources["HighlightBrush"];
                this.currentRectangle = r;
            }
        }
    }
}
